unit StockParamCollect;

interface

uses
  Contnrs, SysUtils, Classes, Forms, SyncObjs, xmldom, XMLIntf, msxmldom,
  XMLDoc, Dialogs;

type
  TEntry = class
  protected
    FName, FCaption: String;
    procedure Init(); virtual;
  public
    property Name: String read FName;
    property Caption: String read FCaption;
    constructor Create(AName: String); overload; virtual;
    constructor Create(AName, ACaption: String); overload; virtual;
  end;

  TStockParam = class(TEntry)
  public
    function ToString(): String; virtual; abstract;
    function getMaxCount(): Integer; virtual; abstract;
    function Conformity(AParam: TStockParam): Integer; virtual; abstract;
    function Equals(AParacCollect: TStockParam): Boolean; virtual; abstract;
  end;

  TStockPerParam = class(TStockParam)
  private
    PerArea: Array [0 .. 21] of Integer;
    
  protected
    procedure Init(); override;
  public
    procedure AddPerValue(APer: Integer);
    function getMaxCount(): Integer; override;
    function Conformity(AParam: TStockParam): Integer; override;
    procedure SetValue(AIndex,ACount: Integer);
    function getSingleIndex(): Integer;
    function ToString(): String; override;
    function Equals(AParacCollect: TStockParam): Boolean;
  end;

  TStockBoolParam = class(TStockParam)
  private
    TrueCount: Integer;
    FalseCount: Integer;
  protected
    procedure Init(); override;
  public
    property PTrueCount: Integer read TrueCount;
    property PFalseCount: Integer read FalseCount;
    procedure AddBoolValue(AValue: Boolean);
    procedure SetValue(AValue: Boolean; ACount: Integer);
    function getMaxCount(): Integer; override;
    function Conformity(AParam: TStockParam): Integer; override;
    function ToString(): String; override;
    function Equals(AParacCollect: TStockParam): Boolean; override;
  end;

  TStockParamCollect = class(TStockParam)
  private
    Params: TObjectList;
    procedure Init(); override;
  public
    function Get(AName: String): TStockParam;
    function Equals(AParacCollect: TStockParam): Boolean; override;
    function ToString(): String; override;
    procedure AddPerParamValue(AName: String; APer: Integer);
    procedure AddBoolParamValue(AName: String; AValue: Boolean);
    procedure AddStockParam(AStockParam: TStockParam);
    function getMaxCount(): Integer; override;
    function Conformity(AParam: TStockParam): Integer; override;
    procedure Clear();
    destructor Destroy(); override;
  end;

  TStockTotalParam = class
  private
    Params: TObjectList;
    iCount: Integer;
    procedure Init();
    procedure IncICount();
  public
    function Get(AName: String): TStockParamCollect;
    function Equals(AParacCollect: TStockTotalParam): Boolean;
    procedure AddPerParamValue(ATotalName, AName: String; APer: Integer);
    procedure AddBoolParamValue(ATotalName, AName: String; AValue: Boolean);
    procedure SaveToFile(AFileName: String);
    procedure LoadFromFile(AFileName: String);
    function ConformityPer(AParam: TStockTotalParam): Integer;
    constructor Create(); overload;
    destructor Destroy(); override;
  end;

const
  SAVE_FILE_COUNT = 500000;
  SAVE_FILE_NAME = 'PARAM_LOG.txt';

var
  CodePoolCS: TCriticalSection;

implementation

{ TStockParamCollect }

procedure TStockParamCollect.AddBoolParamValue(AName: String; AValue: Boolean);
begin
  (Get(AName) as TStockBoolParam).AddBoolValue(AValue);
end;

procedure TStockParamCollect.AddPerParamValue(AName: String; APer: Integer);
begin
  (Get(AName) as TStockPerParam).AddPerValue(APer);
end;

procedure TStockParamCollect.AddStockParam(AStockParam: TStockParam);
begin
  Params.Add(AStockParam);
end;

procedure TStockParamCollect.Clear;
begin
  Params.Clear;
end;

function TStockParamCollect.Conformity(AParam: TStockParam): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to Params.Count -1 do
  begin
    Result := Result + 
      (Params[i] as TStockParam).Conformity((AParam as TStockParamCollect).Get((Params[i] as TStockParam).Name));
  end;
end;

destructor TStockParamCollect.Destroy;
begin
  Params.Free;
  inherited;
end;

function TStockParamCollect.Equals(AParacCollect: TStockParam): Boolean;
begin
  // TODO: 1
end;

function TStockParamCollect.Get(AName: String): TStockParam;
var
  i: Integer;
begin
  Result := nil;
  for i := 0 to Params.Count - 1 do
  begin
    if (Params[i] as TStockParam).Name = AName then
    begin
      Result := Params[i] as TStockParam;
      Exit;
    end;
  end;
end;

function TStockParamCollect.getMaxCount: Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to Params.Count -1 do
  begin
    Result := Result + (Params[i] as TStockParam).getMaxCount;
  end;
end;

procedure TStockParamCollect.Init;
begin
  inherited;
  Params := TObjectList.Create(True);
  Params.Add(TStockPerParam.Create('AVGOpenPer'));
  Params.Add(TStockPerParam.Create('AVGClosePer'));
  Params.Add(TStockPerParam.Create('AVGHighPer'));
  Params.Add(TStockPerParam.Create('AVGLowPer'));
  Params.Add(TStockPerParam.Create('MaxOpenPer'));
  Params.Add(TStockPerParam.Create('MaxClosePer'));
  Params.Add(TStockPerParam.Create('MinOpenPer'));
  Params.Add(TStockPerParam.Create('MinClosePer'));
  Params.Add(TStockPerParam.Create('MaxHighVolumePer'));
  Params.Add(TStockPerParam.Create('MinLowVolumePer'));
  Params.Add(TStockPerParam.Create('GetAVGVolumePer'));
  Params.Add(TStockPerParam.Create('GetMaxVolumePer'));
  Params.Add(TStockPerParam.Create('GetMinVolumePer'));
  Params.Add(TStockPerParam.Create('HightThanCountPer'));
  Params.Add(TStockPerParam.Create('LowThanCountPer'));
  Params.Add(TStockPerParam.Create('HightAndLowThanCountPer'));
  Params.Add(TStockPerParam.Create('HightThanVolumePer'));
  Params.Add(TStockPerParam.Create('LowThanVolumePer'));
  Params.Add(TStockPerParam.Create('HightAndLowVolumePer'));
  Params.Add(TStockPerParam.Create('JumpOpenCountPer'));
  Params.Add(TStockPerParam.Create('JumpOpenSunCountPer'));
  Params.Add(TStockPerParam.Create('JumpOpenMoonCountPer'));
  Params.Add(TStockPerParam.Create('DownOpenCountPer'));
  Params.Add(TStockPerParam.Create('DownOpenSunCountPer'));
  Params.Add(TStockPerParam.Create('DownOpenMoonCountPer'));
  Params.Add(TStockPerParam.Create('MaxKeepSunCountPer'));
  Params.Add(TStockPerParam.Create('MaxKeepMoonCountPer'));
  Params.Add(TStockPerParam.Create('SunMoonChangeCountPer'));
  Params.Add(TStockPerParam.Create('AnitTotalSunCountPer'));
  Params.Add(TStockPerParam.Create('AnitTotalMoonCountPer'));
  Params.Add(TStockPerParam.Create('EqSunTotalCountPer'));
  Params.Add(TStockPerParam.Create('EqMoonTotalCountPer'));
  Params.Add(TStockPerParam.Create('JumpOpenVolumePer'));
  Params.Add(TStockPerParam.Create('JumpOpenSunVolumePer'));
  Params.Add(TStockPerParam.Create('JumpOpenMoonVolumePer'));
  Params.Add(TStockPerParam.Create('DownOpenVolumePer'));
  Params.Add(TStockPerParam.Create('DownOpenSunVolumePer'));
  Params.Add(TStockPerParam.Create('DownOpenMoonVolumePer'));
  Params.Add(TStockPerParam.Create('MaxKeepSunVolumePer'));
  Params.Add(TStockPerParam.Create('MaxKeepMoonVolumePer'));
  Params.Add(TStockPerParam.Create('AnitTotalSunVolumePer'));
  Params.Add(TStockPerParam.Create('AnitTotalMoonVolumePer'));
  Params.Add(TStockPerParam.Create('EqSunTotalVolumePer'));
  Params.Add(TStockPerParam.Create('EqMoonTotalVolumePer'));
  Params.Add(TStockPerParam.Create('SunCountPer'));
  Params.Add(TStockPerParam.Create('MoonCountPer'));
  Params.Add(TStockPerParam.Create('SunVolumePer'));
  Params.Add(TStockPerParam.Create('MoonVolumePer'));

  Params.Add(TStockPerParam.Create('GetMaxPriceRisePer'));
  Params.Add(TStockPerParam.Create('GetCloseRisePer'));
  Params.Add(TStockPerParam.Create('GetLowRisePer'));

  Params.Add(TStockBoolParam.Create('HighThan'));
  Params.Add(TStockBoolParam.Create('LowThan'));
  Params.Add(TStockBoolParam.Create('HingAndLowThan'));
  Params.Add(TStockBoolParam.Create('JumpOpen'));
  Params.Add(TStockBoolParam.Create('DownOpen'));
  Params.Add(TStockBoolParam.Create('AntiSunORMoonTotal'));
  Params.Add(TStockBoolParam.Create('EqTotalData'));
  Params.Add(TStockBoolParam.Create('IsSun'));
end;

function TStockParamCollect.ToString: String;
var
  i: Integer;
begin
  Result := '<block name="' + Name + '">' + #13#10;
  for i := 0 to Params.Count - 1 do
  begin
    Result := Result + (Params[i] as TStockParam).ToString;
  end;
  Result := Result + '</block>' + #13#10;
end;

{ TStockPerParam }

procedure TStockPerParam.AddPerValue(APer: Integer);
var
  Index: Integer;
begin
  Index := Trunc(ABS(APer) / 10);
  if APer >= 0 then
  begin
    Index := 10 + Index;
    if index > 21 then
      index := 21;
  end
  else
  begin
    Index := 10 - Index;
    if Index < 0 then
      Index := 0;
  end;
  PerArea[Index] := PerArea[Index] + 1;
end;

procedure TStockPerParam.SetValue(AIndex, ACount: Integer);
begin
  PerArea[AIndex] := ACount;
end;

function TStockPerParam.Conformity(AParam: TStockParam): Integer;
begin
  Result := PerArea[(AParam as TStockPerParam).getSingleIndex];
end;

function TStockPerParam.Equals(AParacCollect: TStockParam): Boolean;
begin

end;

function TStockPerParam.getMaxCount: Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to 21 do
    if PerArea[i] > Result then
    begin
      Result := PerArea[i];
    end;

end;

function TStockPerParam.getSingleIndex: Integer;
var
  i: Integer;
begin
  for i := 0 to 21 do
    if PerArea[i] > 0 then
    begin
      Result := i;
      Exit;
    end;
end;

procedure TStockPerParam.Init;
var
  i: Integer;
begin
  inherited;
  for i := 0 to 21 do
    PerArea[i] := 0;
end;

function TStockPerParam.ToString: String;
var
  i: Integer;
begin
  Result := '<params name ="' + Name + '" type="per">';
  for i := 0 to 21 do
    Result := Result + #13#10 + '<param name = "PER_' + IntToStr(i) +
      '" value="' + IntToStr(PerArea[i]) + '" index="' + IntToStr(i) + '" />';
  Result := Result + '</params>' + #13#10;
end;

{ TStockTotalParam }

procedure TStockTotalParam.AddBoolParamValue(ATotalName, AName: String;
  AValue: Boolean);
begin
  CodePoolCS.Enter;
  try
    Get(ATotalName).AddBoolParamValue(AName, AValue);
    IncICount();
  finally
    CodePoolCS.Leave;
  end;
end;

procedure TStockTotalParam.AddPerParamValue(ATotalName, AName: String;
  APer: Integer);
begin
  CodePoolCS.Enter;
  try
    Get(ATotalName).AddPerParamValue(AName, APer);
    IncICount();
  finally
    CodePoolCS.Leave;
  end;
end;

function TStockTotalParam.ConformityPer(AParam: TStockTotalParam): Integer;
var
  TotalCount, cc: Integer;
  i: Integer;
begin
  cc := 0;
  TotalCount := 0;
  for i := 0 to Params.Count - 1 do
  begin
    cc := cc + (Params[i] as TStockParamCollect).Conformity( AParam.Get((Params[i] as TStockParamCollect).Name));
    TotalCount := TotalCount + (Params[i] as TStockParamCollect).getMaxCount;
  end;
  //ShowMessage(IntToStr(cc));
  //ShowMessage(IntToStr(TotalCount));
  Result := Trunc((cc / TotalCount) * 100);    
end;

constructor TStockTotalParam.Create;
begin
  CodePoolCS := TCriticalSection.Create;
  Init();
end;

destructor TStockTotalParam.Destroy;
begin
  Params.Free;
  CodePoolCS.Free;
  inherited;
end;

function TStockTotalParam.Equals(AParacCollect: TStockTotalParam): Boolean;
begin

end;

function TStockTotalParam.Get(AName: String): TStockParamCollect;
var
  i: Integer;
begin
  Result := nil;
  for i := 0 to Params.Count - 1 do
  begin
    if (Params[i] as TStockParamCollect).Name = AName then
    begin
      Result := Params[i] as TStockParamCollect;
      Exit;
    end;
  end;
end;

procedure TStockTotalParam.IncICount;
begin
  iCount := iCount + 1;
  if iCount >= SAVE_FILE_COUNT then
  begin
    iCount := 0;
    SaveToFile(ExtractFilePath(Application.ExeName) + SAVE_FILE_NAME);
  end;
end;

procedure TStockTotalParam.Init;
begin
  Params := TObjectList.Create(True);
  iCount := 0;
  Params.Add(TStockParamCollect.Create('30Day'));

  Params.Add(TStockParamCollect.Create('5OF30Day1'));
  Params.Add(TStockParamCollect.Create('5OF30Day2'));
  Params.Add(TStockParamCollect.Create('5OF30Day3'));
  Params.Add(TStockParamCollect.Create('5OF30Day4'));
  Params.Add(TStockParamCollect.Create('5OF30Day5'));
  Params.Add(TStockParamCollect.Create('5OF30Day6'));

  Params.Add(TStockParamCollect.Create('3OF30Day1'));
  Params.Add(TStockParamCollect.Create('3OF30Day2'));
  Params.Add(TStockParamCollect.Create('3OF30Day3'));
  Params.Add(TStockParamCollect.Create('3OF30Day4'));
  Params.Add(TStockParamCollect.Create('3OF30Day5'));
  Params.Add(TStockParamCollect.Create('3OF30Day6'));
  Params.Add(TStockParamCollect.Create('3OF30Day7'));
  Params.Add(TStockParamCollect.Create('3OF30Day8'));
  Params.Add(TStockParamCollect.Create('3OF30Day9'));
  Params.Add(TStockParamCollect.Create('3OF30Day10'));

  Params.Add(TStockParamCollect.Create('5Day'));

  Params.Add(TStockParamCollect.Create('3Day'));
end;

procedure TStockTotalParam.LoadFromFile(AFileName: String);
var
  inXmlDoc: IXMLDocument;
  xRoot, xBlock, xParams, xParam: IXMLNode;
  pc: TStockParamCollect;
  spp: TStockPerParam;
  sbp: TStockBoolParam;
begin
  inXmlDoc := TXMLDocument.Create(nil);
  try
    inXmlDoc.LoadFromFile(AFileName);
    inXmlDoc.Active := true;

    Params.Clear;
    xRoot := inXmlDoc.DocumentElement;
    xBlock := xRoot.ChildNodes.First;
    while xBlock <> nil do
    begin
      xParams := xBlock.ChildNodes.First;
      pc := TStockParamCollect.Create(xBlock.Attributes['name']);
      pc.Clear;
      while xParams <> nil do
      begin
        
        if xParams.Attributes['type'] = 'per' then
        begin          
          spp := TStockPerParam.Create(xParams.Attributes['name']);
          xParam := xParams.ChildNodes.First;
          while xParam <> nil do
          begin
            spp.SetValue(xParam.Attributes['index'],xParam.Attributes['value']);
            xParam := xParam.NextSibling;
          end;
          pc.AddStockParam(spp);
        end
        else
        begin
          sbp := TStockBoolParam.Create(xParams.Attributes['name']);
          xParam := xParams.ChildNodes.First;
          while xParam <> nil do
          begin
            if xParam.Attributes['name'] = 'TRUE_COUNT' then
              sbp.SetValue(True,xParam.Attributes['value'])
            else
              sbp.SetValue(False,xParam.Attributes['value']);
            xParam := xParam.NextSibling;
          end;
          pc.AddStockParam(sbp);
        end;

        xParams := xParams.NextSibling;
      end;
      Params.Add(pc);
      xBlock := xBlock.NextSibling;
    end;
  finally
    //XmlDoc.Free;
  end;
end;

procedure TStockTotalParam.SaveToFile(AFileName: String);
var
  sf: TStringList;
  i: Integer;
begin
  sf := TStringList.Create;
  try
    sf.Add('<?xml version="1.0" encoding="UTF-8"?><analyze>');
    for i := 0 to Params.Count - 1 do
      sf.Add((Params[i] as TStockParam).ToString);
    sf.Add('</analyze>');
    sf.SaveToFile(AFileName);
  finally
    sf.Free;
  end;
end;

{ TEntry }

constructor TEntry.Create(AName: String);
begin
  FName := AName;
  Init();
end;

constructor TEntry.Create(AName, ACaption: String);
begin
  Create(AName);
  FCaption := ACaption;
end;

procedure TEntry.Init;
begin

end;

{ TStockBoolParam }

procedure TStockBoolParam.AddBoolValue(AValue: Boolean);
begin
  if AValue then
    TrueCount := TrueCount + 1
  else
    FalseCount := FalseCount + 1;
end;

function TStockBoolParam.Conformity(AParam: TStockParam): Integer;
begin
  if (AParam as TStockBoolParam).PTrueCount > 0  then
  begin
    Result := (TrueCount div 10);
  end
  else
    Result := (FalseCount div 10);
  
end;

function TStockBoolParam.Equals(AParacCollect: TStockParam): Boolean;
begin

end;

function TStockBoolParam.getMaxCount: Integer;
begin
  if TrueCount > FalseCount then
    Result :=  (TrueCount div 10)
  else
    Result := (FalseCount div 10);
end;

procedure TStockBoolParam.Init;
begin
  inherited;
  TrueCount := 0;
  FalseCount := 0;
end;

procedure TStockBoolParam.SetValue(AValue: Boolean; ACount: Integer);
begin
  if AValue then
    TrueCount := ACount
  else
    FalseCount := ACount;
end;

function TStockBoolParam.ToString: String;
begin
  Result := '<params name ="' + Name + '" type="bool">' + #13#10 +
    '<param name="TRUE_COUNT" value="' + IntToStr(TrueCount) + '"/>' +
    #13#10 + '<param name="FALSE_COUNT"  value="' + IntToStr(FalseCount) +
    '"/>' + #13#10 + '</params>' + #13#10;
end;

end.
